/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * CategoryPanelFormatters.java
 *
 * Created on Jan 20, 2009, 3:30:49 PM
 */

package org.netbeans.modules.debugger.jpda.ui.options;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import org.netbeans.api.debugger.Properties;
import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
import org.netbeans.modules.options.java.api.JavaOptions;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotificationLineSupport;
import org.openide.NotifyDescriptor;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;

/**
 *
 * @author martin
 */
@OptionsPanelController.Keywords(keywords={"#CategoryPanelFormatters.kw1",
                                           "#CategoryPanelFormatters.kw2"},
                                 location=JavaOptions.JAVA, tabTitle="#LBL_JavaDebugger")
class CategoryPanelFormatters extends StorablePanel {

    private static final String COPY1 = " (copy"; // NOI18N
    private static final String COPY2 = ")";      // NOI18N

    /** Creates new form CategoryPanelFormatters */
    public CategoryPanelFormatters() {
        initComponents();
        initFormattersList();
        loadSelectedFormatter(null);
        formatValueEditorPane.setBackground(testChildrenTextField.getBackground());
        formatChildrenCodeEditorPane.setBackground(testChildrenTextField.getBackground());
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        childrenVarsLabel = new javax.swing.JLabel();
        formatChildrenListScrollPane = new javax.swing.JScrollPane();
        formatChildrenListTable = new javax.swing.JTable();
        formattersScrollPane = new javax.swing.JScrollPane();
        formattersList = new javax.swing.JList();
        formatterNameLabel = new javax.swing.JLabel();
        formatterClassTypesLabel = new javax.swing.JLabel();
        formatterClassTypesTextField = new javax.swing.JTextField();
        formatValueLabel = new javax.swing.JLabel();
        formatValueScrollPane = new javax.swing.JScrollPane();
        formatValueEditorPane = new javax.swing.JEditorPane();
        formatChildrenLabel = new javax.swing.JLabel();
        formatChildrenCodeScrollPane = new javax.swing.JScrollPane();
        formatChildrenCodeEditorPane = new javax.swing.JEditorPane();
        jPanel1 = new javax.swing.JPanel();
        formattersAddButton = new javax.swing.JButton();
        formattersRemoveButton = new javax.swing.JButton();
        formattersMoveUpButton = new javax.swing.JButton();
        formattersMoveDownButton = new javax.swing.JButton();
        editButton = new javax.swing.JButton();
        copyButton = new javax.swing.JButton();
        subtypesLabel = new javax.swing.JLabel();
        testChildrenTextField = new javax.swing.JTextField();
        childrenCodeLabel = new javax.swing.JLabel();
        jSeparator1 = new javax.swing.JSeparator();

        org.openide.awt.Mnemonics.setLocalizedText(childrenVarsLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.childrenVarsLabel.text")); // NOI18N
        childrenVarsLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.childrenVarsLabel.AccessibleContext.accessibleDescription")); // NOI18N

        formatChildrenListScrollPane.setEnabled(false);

        formatChildrenListTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Name", "Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        formatChildrenListScrollPane.setViewportView(formatChildrenListTable);

        formattersScrollPane.setViewportView(formattersList);
        formattersList.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersList.a11y.name")); // NOI18N
        formattersList.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersList.a11y.description")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(formatterNameLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatterNameLabel.text")); // NOI18N

        formatterClassTypesLabel.setLabelFor(formatterClassTypesTextField);
        org.openide.awt.Mnemonics.setLocalizedText(formatterClassTypesLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatterClassTypesLabel.text")); // NOI18N

        formatterClassTypesTextField.setEditable(false);

        formatValueLabel.setLabelFor(formatValueEditorPane);
        org.openide.awt.Mnemonics.setLocalizedText(formatValueLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatValueLabel.text")); // NOI18N

        formatValueEditorPane.setEditable(false);
        formatValueScrollPane.setViewportView(formatValueEditorPane);

        org.openide.awt.Mnemonics.setLocalizedText(formatChildrenLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenLabel.text")); // NOI18N

        formatChildrenCodeEditorPane.setEditable(false);
        formatChildrenCodeScrollPane.setViewportView(formatChildrenCodeEditorPane);

        org.openide.awt.Mnemonics.setLocalizedText(formattersAddButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersAddButton.text")); // NOI18N
        formattersAddButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                formattersAddButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(formattersRemoveButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersRemoveButton.text")); // NOI18N
        formattersRemoveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                formattersRemoveButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(formattersMoveUpButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersMoveUpButton.text")); // NOI18N
        formattersMoveUpButton.setToolTipText(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersMoveButtons.tooltip")); // NOI18N
        formattersMoveUpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                formattersMoveUpButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(formattersMoveDownButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersMoveDownButton.text")); // NOI18N
        formattersMoveDownButton.setToolTipText(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersMoveButtons.tooltip")); // NOI18N
        formattersMoveDownButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                formattersMoveDownButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(editButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.editButton.text")); // NOI18N
        editButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                editButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(copyButton, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.copyButton.text")); // NOI18N
        copyButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                copyButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(formattersMoveDownButton)
            .addComponent(formattersMoveUpButton)
            .addComponent(formattersRemoveButton)
            .addComponent(formattersAddButton)
            .addComponent(editButton)
            .addComponent(copyButton)
        );

        jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copyButton, editButton, formattersAddButton, formattersMoveDownButton, formattersMoveUpButton, formattersRemoveButton});

        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addComponent(formattersAddButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(copyButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(editButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(formattersRemoveButton)
                .addGap(18, 18, 18)
                .addComponent(formattersMoveUpButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(formattersMoveDownButton))
        );

        formattersAddButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersAddButton.AccessibleContext.accessibleDescription")); // NOI18N
        formattersRemoveButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersRemoveButton.AccessibleContext.accessibleDescription")); // NOI18N
        editButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.editButton.AccessibleContext.accessibleDescription")); // NOI18N
        copyButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.copyButton.AccessibleContext.accessibleDescription")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(subtypesLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.subtypesLabel.text")); // NOI18N

        testChildrenTextField.setEditable(false);
        testChildrenTextField.setText(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.testChildrenTextField.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(childrenCodeLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.childrenCodeLabel.text")); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(12, 12, 12)
                        .addComponent(formatterNameLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jSeparator1))
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                                        .addComponent(formatterClassTypesLabel)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(formatterClassTypesTextField))
                                    .addComponent(formatValueLabel, javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(childrenCodeLabel, javax.swing.GroupLayout.Alignment.LEADING))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(subtypesLabel)
                                .addGap(6, 6, 6))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(formattersScrollPane)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(12, 12, 12)
                        .addComponent(formatChildrenLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(testChildrenTextField))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(24, 24, 24)
                        .addComponent(formatValueScrollPane))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(24, 24, 24)
                        .addComponent(formatChildrenCodeScrollPane)))
                .addGap(0, 0, 0))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(formattersScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(formatterNameLabel)
                    .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(formatterClassTypesLabel)
                    .addComponent(formatterClassTypesTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(subtypesLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(formatValueLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(formatValueScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(formatChildrenLabel)
                    .addComponent(testChildrenTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(childrenCodeLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(formatChildrenCodeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 79, Short.MAX_VALUE))
        );

        layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {formattersScrollPane, jPanel1});

        formatterClassTypesLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatterClassTypesLabel.AccessibleContext.accessibleDescription")); // NOI18N
        formatValueLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatValueLabel.AccessibleContext.accessibleDescription")); // NOI18N
        formatChildrenLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenLabel.AccessibleContext.accessibleDescription")); // NOI18N
        subtypesLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.subtypesLabel.AccessibleContext.accessibleDescription")); // NOI18N
        childrenCodeLabel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.childrenCodeLabel.AccessibleContext.accessibleDescription")); // NOI18N
    }// </editor-fold>//GEN-END:initComponents

    private void formattersAddButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formattersAddButtonActionPerformed
        VariablesFormatter f = new VariablesFormatter("");
        final VariableFormatterEditPanel fPanel = new VariableFormatterEditPanel();
        fPanel.load(f);

        fPanel.setFormatterNames(getFormatterNames());
        final Dialog[] dlgPtr = new Dialog[] { null };
        DialogDescriptor formatterEditDescriptor = new DialogDescriptor(
                fPanel,
                NbBundle.getMessage(CategoryPanelFormatters.class, "TTL_AddFormatter"),
                true,
                NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.OK_OPTION,
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        if (e.getSource() == NotifyDescriptor.OK_OPTION) {
                            boolean valid = fPanel.checkValidInput();
                            if (valid) {
                                dlgPtr[0].setVisible(false);
                            }
                        } else {
                            dlgPtr[0].setVisible(false);
                        }
                    }
                });
        formatterEditDescriptor.setClosingOptions(new Object[] {});
        NotificationLineSupport notificationSupport = formatterEditDescriptor.createNotificationLineSupport();
        fPanel.setValidityObjects(formatterEditDescriptor, notificationSupport, false);
        //formatterEditDescriptor.setValid(false);
        Dialog dlg = DialogDisplayer.getDefault().createDialog(formatterEditDescriptor);
        Properties p = Properties.getDefault().getProperties("debugger.options.JPDA");   // NOI18N
        int w = p.getInt("VariableFormatters_AddEdit_WIDTH", -1);                        // NOI18N
        int h = p.getInt("VariableFormatters_AddEdit_HEIGHT", -1);                       // NOI18N
        if (w > 0 && h > 0) {
            dlg.setSize(new Dimension(w, h));
        }
        dlgPtr[0] = dlg;
        dlg.setVisible(true);
        Dimension dim = dlg.getSize();
        p.setInt("VariableFormatters_AddEdit_WIDTH", dim.width);                         // NOI18N
        p.setInt("VariableFormatters_AddEdit_HEIGHT", dim.height);                       // NOI18N
        if (NotifyDescriptor.OK_OPTION.equals(formatterEditDescriptor.getValue())) {
            fPanel.store(f);
            ((DefaultListModel) formattersList.getModel()).addElement(f);
            formattersList.setSelectedValue(f, true);
        }

        /*
        NotifyDescriptor.InputLine nd = new NotifyDescriptor.InputLine(
                NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.addDLG.nameLabel"),
                NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.addDLG.title"));
        DialogDisplayer.getDefault().notify(nd);
        VariablesFormatter f = new VariablesFormatter(nd.getInputText());
        ((DefaultListModel) formattersList.getModel()).addElement(f);
        formattersList.setSelectedValue(f, true);
        //JCheckBox cb = new JCheckBox(nd.getInputText());
        //cb.setSelected(true);
        //filterClassesList.add(cb);
        //filterClassesList.repaint();
        */
    }//GEN-LAST:event_formattersAddButtonActionPerformed

    private void formattersRemoveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formattersRemoveButtonActionPerformed
        int index = formattersList.getSelectedIndex();
        if (index < 0) return ;
        ((DefaultListModel) formattersList.getModel()).remove(index);
        if (index < formattersList.getModel().getSize() || --index >= 0) {
            formattersList.setSelectedIndex(index);
        }
    }//GEN-LAST:event_formattersRemoveButtonActionPerformed

    private void formattersMoveUpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formattersMoveUpButtonActionPerformed
        int index = formattersList.getSelectedIndex();
        if (index <= 0) return ;
        Object obj = ((DefaultListModel) formattersList.getModel()).remove(index);
        ((DefaultListModel) formattersList.getModel()).insertElementAt(obj, index - 1);
        formattersList.setSelectedIndex(index - 1);
    }//GEN-LAST:event_formattersMoveUpButtonActionPerformed

    private void formattersMoveDownButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formattersMoveDownButtonActionPerformed
        int index = formattersList.getSelectedIndex();
        if (index < 0) return ;
        DefaultListModel model = (DefaultListModel) formattersList.getModel();
        if (index >= (model.getSize() - 1)) return ;
        Object obj = model.remove(index);
        model.insertElementAt(obj, index + 1);
        formattersList.setSelectedIndex(index + 1);
    }//GEN-LAST:event_formattersMoveDownButtonActionPerformed

    private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editButtonActionPerformed
        int index = formattersList.getSelectedIndex();
        if (index < 0) return ;
        DefaultListModel model = (DefaultListModel) formattersList.getModel();
        VariablesFormatter f = (VariablesFormatter) model.getElementAt(index);

        VariableFormatterEditPanel fPanel = new VariableFormatterEditPanel();
        fPanel.load(f);

        Set<String> formatterNames = getFormatterNames();
        formatterNames.remove(f.getName());
        fPanel.setFormatterNames(formatterNames);

        DialogDescriptor formatterEditDescriptor = new DialogDescriptor(
                fPanel,
                NbBundle.getMessage(CategoryPanelFormatters.class, "TTL_EditFormatter"),
                true,
                NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.OK_OPTION,
                null);
        NotificationLineSupport notificationSupport = formatterEditDescriptor.createNotificationLineSupport();
        fPanel.setValidityObjects(formatterEditDescriptor, notificationSupport, true);
        //formatterEditDescriptor.setValid(false);
        Dialog dlg = DialogDisplayer.getDefault().createDialog(formatterEditDescriptor);
        Properties p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
        int w = p.getInt("VariableFormatters_AddEdit_WIDTH", -1);                      // NOI18N
        int h = p.getInt("VariableFormatters_AddEdit_HEIGHT", -1);                     // NOI18N
        if (w > 0 && h > 0) {
            dlg.setSize(new Dimension(w, h));
        }
        dlg.setVisible(true);
        Dimension dim = dlg.getSize();
        p.setInt("VariableFormatters_AddEdit_WIDTH", dim.width);                       // NOI18N
        p.setInt("VariableFormatters_AddEdit_HEIGHT", dim.height);                     // NOI18N
        if (NotifyDescriptor.OK_OPTION.equals(formatterEditDescriptor.getValue())) {
            fPanel.store(f);
            checkBoxComponents.put(f, new JCheckBox(f.getName(), f.isEnabled()));
            ((DefaultListModel) formattersList.getModel()).setElementAt(f, index);
            //formattersList.repaint();
            formattersList.setSelectedValue(f, true);
            loadSelectedFormatter(f);
        }
    }//GEN-LAST:event_editButtonActionPerformed

    private void copyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyButtonActionPerformed
        int index = formattersList.getSelectedIndex();
        if (index < 0) return ;
        DefaultListModel model = (DefaultListModel) formattersList.getModel();
        VariablesFormatter f = (VariablesFormatter) model.getElementAt(index);
        VariablesFormatter f2 = f.clone();

        Set<String> formatterNames = getFormatterNames();
        String name = f2.getName();
        while (formatterNames.contains(name)) {
            boolean isCopied = name.contains(COPY1) && name.endsWith(COPY2);
            int nc = 0;
            if (isCopied) {
                int i1 = name.lastIndexOf(COPY1) + COPY1.length();
                int i2 = name.length() - COPY2.length();
                if (i1 == i2) {
                   nc = 1;
                } else {
                    String ncs = name.substring(i1, i2);
                    try {
                        nc = Integer.parseInt(ncs);
                    } catch (NumberFormatException nfex) {
                        isCopied = false;
                    }
                }
            }
            if (isCopied) {
                nc++;
                name = name.substring(0, name.lastIndexOf(COPY1)) + COPY1 + nc + COPY2;
            } else {
                name = name + COPY1 + COPY2;
            }
        }
        f2.setName(name);
        ((DefaultListModel) formattersList.getModel()).insertElementAt(f2, index);
        formattersList.setSelectedValue(f2, true);
        
}//GEN-LAST:event_copyButtonActionPerformed

    private Set<String> getFormatterNames() {
        Set<String> formatterNames = new HashSet<String>();
        ListModel formattersModel = formattersList.getModel();
        int n = formattersModel.getSize();
        for (int i = 0; i < n; i++) {
            VariablesFormatter vf = (VariablesFormatter) formattersModel.getElementAt(i);
            formatterNames.add(vf.getName());
        }
        return formatterNames;
    }

    private void initFormattersList() {
        formattersList.setCellRenderer(new ListCellRenderer() {
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                VariablesFormatter vf = (VariablesFormatter) value;
                JCheckBox cb = checkBoxComponents.get(vf);
                if (cb == null) {
                    cb = new JCheckBox(vf.getName(), vf.isEnabled());
                    cb.setOpaque(true);
                    checkBoxComponents.put(vf, cb);
                }
                cb.setEnabled(list.isEnabled());
                cb.setFont(list.getFont());
                cb.setBackground(isSelected ? list.getSelectionBackground() : list.getBackground());
                cb.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
                return cb;
            }
        });
        formattersList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent event) {
                JList list = (JList) event.getSource();
                // Get index of item clicked
                int index = list.locationToIndex(event.getPoint());
                if (index < 0) return ;
                int height = list.getUI().getCellBounds(formattersList, index, index).height;
                Point cellLocation = list.getUI().indexToLocation(formattersList, index);
                int x = event.getPoint().x - cellLocation.x;
                if (x >= 0 && x <= height) {
                    VariablesFormatter vf = (VariablesFormatter) list.getModel().getElementAt(index);
                    // Toggle selected state
                    vf.setEnabled(!vf.isEnabled());
                    JCheckBox cb = checkBoxComponents.get(vf);
                    cb.setSelected(vf.isEnabled());
                    // Repaint cell
                    list.repaint(list.getCellBounds(index, index));
                }
            }
        });
        formattersList.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                //Remember the last selection, store values to the last selected format and load values for the new one.
                int index = formattersList.getSelectedIndex();
                if (index >= 0) {
                    selectedVariablesFormatter = (VariablesFormatter) formattersList.getModel().getElementAt(index);
                } else {
                    selectedVariablesFormatter = null;
                }
                boolean isDefaultFormatter = selectedVariablesFormatter != null && selectedVariablesFormatter.isDefault();
                formattersRemoveButton.setEnabled(index >= 0 && !isDefaultFormatter);
                editButton.setEnabled(index >= 0 && !isDefaultFormatter);
                copyButton.setEnabled(index >= 0);
                if (index >= 0 && isDefaultFormatter) {
                    formattersRemoveButton.setToolTipText(NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formattersRemoveButton.tooltip"));
                    editButton.setToolTipText(NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.editButton.tooltip"));
                } else {
                    formattersRemoveButton.setToolTipText(null);
                    editButton.setToolTipText(null);
                }
                formattersMoveDownButton.setEnabled(index >= 0 && index < (formattersList.getModel().getSize() - 1));
                formattersMoveUpButton.setEnabled(index >= 1);
                /*if (selectedVariablesFormatter != null) {
                    storeSelectedFormatter(selectedVariablesFormatter);
                }*/
                loadSelectedFormatter(selectedVariablesFormatter);
            }
        });
        formattersList.setModel(new DefaultListModel());
        formattersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    }

    private void loadSelectedFormatter(VariablesFormatter f) {
        if (f == null) {
            formatterClassTypesTextField.setText("");
            subtypesLabel.setVisible(false);
            //formatterClassTypesSubtypesCheckBox.setSelected(false);
            formatValueEditorPane.setText("");
            formatChildrenCodeEditorPane.setText("");
            formatChildrenListTable.setModel(new DefaultTableModel(new String[0][2], tableColumnNames));
            testChildrenTextField.setText("");
            //childrenExpandExpressionTextField.setText("");
        } else {
            formatterClassTypesTextField.setText(f.getClassTypesCommaSeparated());
            subtypesLabel.setVisible(f.isIncludeSubTypes());
            //formatterClassTypesSubtypesCheckBox.setSelected(f.isIncludeSubTypes());
            formatValueEditorPane.setText(f.getValueFormatCode());
            if (f.isUseChildrenVariables()) {
                if (isChildrenCodeDisplayed) {
                    //System.err.println("Replacing "+childrenCodeLabel+" with "+childrenVarsLabel);
                    ((GroupLayout) getLayout()).replace(childrenCodeLabel, childrenVarsLabel);
                    //System.err.println("Replacing "+formatChildrenCodeScrollPane+" with "+formatChildrenListScrollPane);
                    ((GroupLayout) getLayout()).replace(formatChildrenCodeScrollPane, formatChildrenListScrollPane);
                    isChildrenCodeDisplayed = false;
                }
                Map<String, String> childrenVariables = f.getChildrenVariables();
                int n = childrenVariables.size();
                Iterator<Map.Entry<String, String>> childrenVariablesEntries = childrenVariables.entrySet().iterator();
                String[][] tableData = new String[n][2];
                for (int i = 0; i < n; i++) {
                    Map.Entry<String, String> e = childrenVariablesEntries.next();
                    tableData[i][0] = e.getKey();
                    tableData[i][1] = e.getValue();
                }
                DefaultTableModel childrenVarsModel = new DefaultTableModel(tableData, tableColumnNames) {
                    @Override
                    public Class<?> getColumnClass(int columnIndex) {
                        return String.class;
                    }
                    @Override
                    public boolean isCellEditable(int rowIndex, int columnIndex) {
                        return false;
                    }
                };
                formatChildrenListTable.setModel(childrenVarsModel);
                //DisablingCellRenderer.apply(formatChildrenListTable, VariableFormatterEditPanel.getDisabledFieldBackground());
            } else {
                if (!isChildrenCodeDisplayed) {
                    //System.err.println("Replacing "+childrenVarsLabel+" with "+childrenCodeLabel);
                    ((GroupLayout) getLayout()).replace(childrenVarsLabel, childrenCodeLabel);
                    //System.err.println("Replacing "+formatChildrenListScrollPane+" with "+formatChildrenCodeScrollPane);
                    ((GroupLayout) getLayout()).replace(formatChildrenListScrollPane, formatChildrenCodeScrollPane);
                    isChildrenCodeDisplayed = true;
                }
                formatChildrenCodeEditorPane.setText(f.getChildrenFormatCode());
            }
            String childrenExpandTest = f.getChildrenExpandTestCode();
            if (childrenExpandTest != null && childrenExpandTest.trim().length() > 0) {
                testChildrenTextField.setVisible(true);
                testChildrenTextField.setText(childrenExpandTest);
                org.openide.awt.Mnemonics.setLocalizedText(formatChildrenLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenLabel.text")); // NOI18N
                if (isChildrenCodeDisplayed) {
                    childrenCodeLabel.setVisible(true);
                } else {
                    childrenVarsLabel.setVisible(true);
                }
            } else {
                testChildrenTextField.setVisible(false);
                if (isChildrenCodeDisplayed) {
                    org.openide.awt.Mnemonics.setLocalizedText(formatChildrenLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenLabelNoTestCode.text")); // NOI18N
                    childrenCodeLabel.setVisible(false);
                } else {
                    org.openide.awt.Mnemonics.setLocalizedText(formatChildrenLabel, org.openide.util.NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenLabelNoTestVars.text")); // NOI18N
                    childrenVarsLabel.setVisible(false);
                }
            }
        }
    }

    /*private void storeSelectedFormatter(VariablesFormatter f) {
        f.setName(formatterNameTextField.getText());
        f.setClassTypes(formatterClassTypesTextField.getText());
        f.setIncludeSubTypes(formatterClassTypesSubtypesCheckBox.isSelected());
        f.setValueFormatCode(formatValueEditorPane.getText());
        f.setChildrenFormatCode(formatChildrenCodeEditorPane.getText());
        TableModel tableModel = formatChildrenListTable.getModel();
        f.getChildrenVariables().clear();
        for (int i = 0; i < tableModel.getRowCount(); i++) {
            f.addChildrenVariable((String) tableModel.getValueAt(i, 0), (String) tableModel.getValueAt(i, 1));
        }
        f.setUseChildrenVariables(formatChildrenAsListRadioButton.isSelected());
        f.setChildrenExpandTestCode(childrenExpandExpressionTextField.getText());
    }*/
    
    @Override
    public void load() {
        VariablesFormatter[] formatters = VariablesFormatter.loadFormatters();
        DefaultListModel filterClassesModel = (DefaultListModel) formattersList.getModel();
        filterClassesModel.clear();
        if (formatters != null) {
            for (int i = 0; i < formatters.length; i++) {
                filterClassesModel.addElement(formatters[i]);
            }
            if (formatters.length > 0) {
                formattersList.setSelectedValue(formatters[0], true);
            }
        }
    }

    @Override
    public void store() {
        /*if (selectedVariablesFormatter != null) {
            storeSelectedFormatter(selectedVariablesFormatter);
        }*/
        Properties p = Properties.getDefault().getProperties("debugger.options.JPDA");
        ListModel formattersModel = formattersList.getModel();
        VariablesFormatter[] formatters = new VariablesFormatter[formattersModel.getSize()];
        for (int i = 0; i < formatters.length; i++) {
            VariablesFormatter vf = (VariablesFormatter) formattersModel.getElementAt(i);
            formatters[i] = vf;
        }
        p.setArray("VariableFormatters", formatters);
    }

    @Override
    public boolean isChanged() {
        ListModel formattersModel = formattersList.getModel();
        VariablesFormatter[] formatters = new VariablesFormatter[formattersModel.getSize()];
        for (int i = 0; i < formatters.length; i++) {
            formatters[i] = (VariablesFormatter) formattersModel.getElementAt(i);
        }
        VariablesFormatter[] saved = VariablesFormatter.loadFormatters();
        if(saved == null) {
            return false;
        }
        if(saved.length != formatters.length) {
            return true;
        }
        for (int i = 0; i < saved.length; i++) {
            VariablesFormatter savedFormatter = saved[i];
            VariablesFormatter currentFormatter = (VariablesFormatter) formattersModel.getElementAt(i);
            if(!areVariablesFormattersEqual(savedFormatter, currentFormatter)) {
                return true;
            }
        }
        return false;
    }
    
    private boolean areVariablesFormattersEqual(VariablesFormatter savedFormatter, VariablesFormatter currentFormatter) {
        return savedFormatter.getName().equals(currentFormatter.getName())
                && savedFormatter.getChildrenExpandTestCode().equals(currentFormatter.getChildrenExpandTestCode())
                && savedFormatter.getChildrenFormatCode().equals(currentFormatter.getChildrenFormatCode())
                && savedFormatter.getChildrenVariables().equals(currentFormatter.getChildrenVariables())
                && Arrays.equals(savedFormatter.getClassTypes(), currentFormatter.getClassTypes())
                && savedFormatter.getClassTypesCommaSeparated().equals(currentFormatter.getClassTypesCommaSeparated())
                && savedFormatter.getValueFormatCode().equals(currentFormatter.getValueFormatCode())
                && savedFormatter.isDefault() == currentFormatter.isDefault()
                && savedFormatter.isEnabled() == currentFormatter.isEnabled()
                && savedFormatter.isIncludeSubTypes() == currentFormatter.isIncludeSubTypes()
                && savedFormatter.isUseChildrenVariables() == currentFormatter.isUseChildrenVariables();
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel childrenCodeLabel;
    private javax.swing.JLabel childrenVarsLabel;
    private javax.swing.JButton copyButton;
    private javax.swing.JButton editButton;
    private javax.swing.JEditorPane formatChildrenCodeEditorPane;
    private javax.swing.JScrollPane formatChildrenCodeScrollPane;
    private javax.swing.JLabel formatChildrenLabel;
    private javax.swing.JScrollPane formatChildrenListScrollPane;
    private javax.swing.JTable formatChildrenListTable;
    private javax.swing.JEditorPane formatValueEditorPane;
    private javax.swing.JLabel formatValueLabel;
    private javax.swing.JScrollPane formatValueScrollPane;
    private javax.swing.JLabel formatterClassTypesLabel;
    private javax.swing.JTextField formatterClassTypesTextField;
    private javax.swing.JLabel formatterNameLabel;
    private javax.swing.JButton formattersAddButton;
    private javax.swing.JList formattersList;
    private javax.swing.JButton formattersMoveDownButton;
    private javax.swing.JButton formattersMoveUpButton;
    private javax.swing.JButton formattersRemoveButton;
    private javax.swing.JScrollPane formattersScrollPane;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JLabel subtypesLabel;
    private javax.swing.JTextField testChildrenTextField;
    // End of variables declaration//GEN-END:variables
    private final Map<VariablesFormatter, JCheckBox> checkBoxComponents = new WeakHashMap<VariablesFormatter, JCheckBox>();
    private VariablesFormatter selectedVariablesFormatter;
    private final String[] tableColumnNames = new String[] {
        NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenListTable.Name"),
        NbBundle.getMessage(CategoryPanelFormatters.class, "CategoryPanelFormatters.formatChildrenListTable.Value")
    };
    private boolean isChildrenCodeDisplayed = true;

}
